//
// Created by QU on 24-9-5.
//

#pragma once

#include <memory>
#include <iostream>
#include <map>
#include <queue>
#include <thread>
#include <chrono>

#include <boost/asio.hpp>
#include <boost/uuid.hpp>

class MsgNode;
class Server;
class Session;

namespace asio = boost::asio;

using tcp = asio::ip::tcp;
using udp = asio::ip::udp;

class Session : public std::enable_shared_from_this<Session>
{
public:
    Session(asio::io_context& io_context, Server& server);
    ~Session();
    tcp::socket& socket();
    void start();

    std::string& getUuid();

    void Send(std::byte* message, size_t max_length);

private:
    void StartAsyncWrite();

    static void PrintBufferContent(const std::byte* data, size_t size);
    static void PrintBufferContentRed(const std::byte* data, size_t size);
    // void handler_read(const boost::system::error_code& error_code, size_t bytes_transferred , std::shared_ptr<Session> self)
    void HandlerRead(const boost::system::error_code& error_code, size_t bytes_transferred);
    void HandlerWrite(const boost::system::error_code& error_code, size_t bytes_transferred);


    Server& server_;
    tcp::socket socket_;

    static constexpr size_t MAX_LENGTH = 1024;
    std::byte data_[MAX_LENGTH] { std::byte { 0 } };
    std::string uuid_;

    std::queue<std::shared_ptr<MsgNode>> send_msg_queue_;
    std::mutex send_mutex_;

    bool header_parse_flag = false;
    std::shared_ptr<MsgNode> recv_msg_node_;
};

class Server
{
public:
    Server(asio::io_context& io_context, unsigned short port);
    void ClearSessionWithUUID(std::string const& uuid);

private:
    void start_accept();
    void handle_accept(const std::shared_ptr<Session>& new_session_p, const boost::system::error_code& error_code);

    // io_context 不允许拷贝.   我们这里整一个引用.
    asio::io_context& io_context_;
    asio::ip::tcp::acceptor acceptor_;

    std::map<std::string, std::shared_ptr<Session>> session_map_;
};

// 我们希望将原来的响应式的服务器改成一个全双工服务器, 向外提供一个接口, 但是这个接口可能会有一些问题(05里有提到)
// 如果是带有发送功能的异步服务器, 可能会有tcp发一半又到另一半的问题, 为了实现顺序性.
/*
 * //用户发送数据
 * WriteToSocketErr("Hello World!");
 * //用户无感知下层调用情况又一次发送了数据
 * WriteToSocketErr("Hello World!");
 *
 *   那么很可能第一次只发送了Hello，后面的数据没发完，第二次发送了Hello World!之后又发送了World!
 *   所以对端收到的数据很可能是”HelloHello World! World!”
 *   那怎么解决这个问题呢，我们可以通过队列保证应用层的发送顺序。我们在Session中定义一个发送队列，然后重新定义正确的异步发送函数和回调处理
 */
// 我们需要将这个服务器改造成带有发送队列功能的服务器, 并且实现收发分离.

class MsgNode
{
    friend class Session;

    static constexpr size_t MAX_LENGTH = 1024 * 2;
    static constexpr size_t HEADER_LENGTH = 2;

public:
    explicit MsgNode(const std::byte* msg, size_t max_len);
    explicit MsgNode(size_t max_len);

    void ClearMsg();

    ~MsgNode();

private:
    size_t max_len_ { 0 }; // 最大长度
    size_t cur_len_ { 0 }; // 当前长度
    std::byte* data_ { nullptr }; // 消息数据指针
};


int main(int argc, char* argv[]);
